package com.ymt.bpm.service.engine.task.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ymt.bpm.dao.MDao;
import com.ymt.bpm.dao.Mapper;
import com.ymt.bpm.engine.EngineConst;
import com.ymt.bpm.engine.bpmn.ProcessUtil;
import com.ymt.bpm.engine.listener.engine.exception.EngineException;
import com.ymt.bpm.model.*;
import com.ymt.bpm.service.engine.bpd.BpdVersionCacheService;
import com.ymt.bpm.service.engine.task.PiService;
import com.ymt.bpm.service.engine.task.TiService;
import com.ymt.bpm.service.platform.websocket.WebSocketService;
import com.ymt.bpm.util.AES;
import com.ymt.bpm.util.Const;
import com.ymt.bpm.util.DateUtil;
import com.ymt.bpm.util.DhxUtil;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.ModelAndView;

import java.io.File;
import java.util.*;

/**
 * Created by Johnny on 2017/4/19.
 */
@Service
public class TiServiceImpl implements TiService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Environment env;

    @Autowired
    private org.activiti.engine.TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private BpdVersionCacheService bvcs;

    @Autowired
    private PiService pis;

    @Autowired
    private MDao dao;


    /**
     * 保存草稿
     * @param tenantId
     * @param loginName
     * @param tiId
     * @param title
     * @param lang
     * @param timezoneOffset
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject saveDraft(String tenantId, String loginName, String tiId,
                                String title, String lang, int timezoneOffset) {
        //查询任务 TI
        PTaskTodo tt = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            tt.setTenantId(Integer.parseInt(tenantId));
        }
        tt.setTiId(AES.decrypt(tiId));
        tt = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", tt);
        tt.setPurpose(2);   //draft

        // 查流程实例 PI
        PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(tt.getPiEid());
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);
        pi.setStatus("00");
        pi.setTitle(title);

        int rs = 0;
        rs += dao.update(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", tt);
        rs += dao.update(Mapper.PPiMapper, "updateByPrimaryKeySelective", pi);

        JSONObject rtn = new JSONObject();
        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 从范本起草
     * @param tenantId
     * @param loginName
     * @param oldTiId
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject draftFromTpl(String tenantId, String loginName, String oldTiId) {
        JSONObject rtn = new JSONObject();

        boolean jsonDataEnabled = Boolean.parseBoolean(env.getProperty("bpmp.fmd-json-bizdata", "false"));

        //查询任务 TI
        PTaskTodo tt = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            tt.setTenantId(Integer.parseInt(tenantId));
        }
        tt.setTiId(oldTiId);
        tt = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", tt);
        //查询 PI
        PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(tt.getPiEid());
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);
        String oldPiEid = pi.getPiEid();

        //校验办理人是不是当前用户
        if (!loginName.equalsIgnoreCase(tt.getUserId()) && !loginName.equalsIgnoreCase(tt.getdUserId())) {
            //TODO 协办人
            rtn.put("msg", "401");
        }

        //get user dept info
        Map<String, Object> deptArgMap = new HashMap<String, Object>();
        deptArgMap.put(Const.TENANT_ID, tenantId);
        deptArgMap.put("userId", loginName);
        POrg org = dao.selectOne(Mapper.POrgMapper, "selectMainOrgOfUser", deptArgMap);

        String newPiEid = UUID.randomUUID().toString();
        String newPiId = UUID.randomUUID().toString();
        String newTiId = UUID.randomUUID().toString();
        //复制TI
        tt.setTiId(newTiId);
        tt.setTiEId(UUID.randomUUID().toString());
        tt.setPiEid(newPiEid);
        tt.setPiId(newPiId);
        tt.setPurpose(PTaskTodo.PURPOSE_DRAFT);
        tt.setCreateTime(DateUtil.getGMTDate());
        int rs = dao.insert(Mapper.PTaskTodoMapper, "insertSelective", tt);
        //复制PI
        pi.setPiEid(newPiEid);
        pi.setPiId(newPiId);
        pi.setCreateTime(DateUtil.getGMTDate());
        pi.setTitle(null);
        pi.setStatus("-1"); //force to temp here
        rs += dao.insert(Mapper.PPiMapper, "insertSelective", pi);
        //复制TODO UserRef
        PUserRef urkey = new PUserRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            urkey.setTenantId(Integer.parseInt(tenantId));
        }
        urkey.setId(oldTiId);
        urkey.setType("1");
        urkey.setUserId(tt.getUserId());
        PUserRef ur = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
        ur.setId(newTiId);
        rs += dao.insert(Mapper.PUserRefMapper, "insert", ur);
        //复制PI UserRef
        urkey.setId(oldPiEid);
        urkey.setType("0");
        ur = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
        ur.setId(newPiEid);
        ur.setDeptId(org.getOrgId());
        ur.setDeptName(org.getOrgName());//TODO role info
        rs += dao.insert(Mapper.PUserRefMapper, "insert", ur);

        //复制业务数据
        if (jsonDataEnabled) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("newPiEid", newPiEid);
            map.put("oldPiEid", oldPiEid);
            rs += dao.insert(Mapper.PDataMMapper, "clone", map);
        } else {
            PDraftTplRef ref = new PDraftTplRef();
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                ref.setTenantId(Integer.parseInt(tenantId));
            }
            ref.setTiId(oldTiId);
            ref = dao.selectOne(Mapper.PDraftTplRefMapper, "selectByPrimaryKey", ref);
            JSONObject models = JSONObject.parseObject(ref.getModels());
            List<Map<String, String>> inserts = new ArrayList<Map<String, String>>();
            for (String model: models.keySet()) {
                StringBuilder insert = new StringBuilder("insert into ")
                        .append(model)
                        .append(" (")
                        .append("TS_ID,");
                JSONObject attrs = models.getJSONObject(model).getJSONObject("attrs");
                String cols = StringUtils.join(attrs.keySet(), ",");
                insert.append(cols)
                        .append(")")
                        .append(" select ")
                        .append("'")
                        .append(newPiEid)
                        .append("',")
                        .append(cols)
                        .append(" from ")
                        .append(model)
                        .append(tenantId!=null?"_"+tenantId:"")
                        .append(" where TS_ID='")
                        .append(oldPiEid)
                        .append("'");
                Map<String, String> map = new HashMap<String, String>();
                map.put("sql", insert.toString());
                inserts.add(map);
            }
            rs += dao.batchInsert(Mapper.CustomSQLMapper, "insert", inserts);
            //复制一份PDraftTplRef供发起流程时修改piEid为真正引擎ID使用
            ref.setTiId(newTiId);
            rs += dao.insert(Mapper.PDraftTplRefMapper, "insert", ref);
        }

        rtn.put("tiId", tt.getTiId());
        rtn.put("result", rs);
        return rtn;
    }

    /**
     * 任务办理界面
     * @param tenantId
     * @param view
     * @param loginName
     * @param tiIdEnc
     * @param contextRealPath
     * @param lang
     * @param timezoneOffset
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public ModelAndView getTodoView(String tenantId, ModelAndView view, String loginName,
                                    String tiIdEnc, String contextRealPath, String lang, int timezoneOffset, String formLocation) {
        String tiId = AES.decrypt(tiIdEnc);

        //查询任务 TI
        PTaskTodo tt = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            tt.setTenantId(Integer.parseInt(tenantId));
        }
        tt.setTiId(tiId);
        tt = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", tt);

        //校验办理人是不是当前用户
        if (!loginName.equalsIgnoreCase(tt.getUserId()) && !loginName.equalsIgnoreCase(tt.getdUserId())) {
            //TODO 协办人
            view.setViewName("/html/error-401.jsp");
            return view;
        }

        // 查流程实例 PI
        PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(tt.getPiEid());
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);
        //流程实例userRef
        PUserRef urkey = new PUserRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            urkey.setTenantId(Integer.parseInt(tenantId));
        }
        urkey.setId(pi.getPiEid());
        urkey.setType(PUserRef.TYPE_PROCESS);
        urkey.setUserId(pi.getUserId());
        PUserRef ur = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
        JSONObject pijo = DhxUtil.toJsonWithDateAli(pi, lang, timezoneOffset);
        pijo.put("userRef", (JSONObject)JSONObject.toJSON(ur));

        //查询version
        JSONObject version = bvcs.getVersionJson(tenantId, pi.getBpdId(), pi.getVersionid());
        version.remove(EngineConst.BPMN);   //remove bpmn for performance issue
        //bpmnConfig
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");
        //activityConf
        JSONObject activityConf = bpmnConfig.getJSONObject(tt.getActivityId());

        //校验form file
        JSONObject formBinding = activityConf.getJSONObject("formBinding");
        String tenantSubDir = tenantId == null ? "0/" : tenantId+"/";
        String formfileName = formBinding.getString("formid") + "-" +
                formBinding.getString("versionid") + "-" + lang + "-" +
                env.getProperty("bpmp.formskin");
        String formfile = "/jsp/forms/" + tenantSubDir + formfileName + ".jsp";
        //check form file
        if (logger.isDebugEnabled()) {
            logger.debug("getTodoView form location=" + formLocation);
        }
        if (!"cdn".equalsIgnoreCase(formLocation)) {
            String realPath = contextRealPath.replace("\\", "/") + formfile.substring(1);
            if (logger.isDebugEnabled()) {
                logger.debug("getTodoView formfile=" + formfile);
                logger.debug("getTodoView realPath=" + realPath);
            }
            File file = new File(realPath);
            if (!file.exists()) {
                view.setViewName("/html/error-msg.jsp?msg=FORMFILE_NOTFOUND");
                //view.addObject("msg", "FORMFILE_NOTFOUND");
                return view;
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("getTodoView tenantSubDir =" + tenantSubDir);
                logger.debug("getTodoView form =" + formfileName);
            }
        }

        //查询bpd
        JSONObject bpd = bvcs.getBpdSimple(tenantId, pi.getBpdId(), lang, timezoneOffset);

        //主流程
        JSONObject pimjo = null;
        //业务输入
        JSONObject bizInput = null;
        //当前环节
        List<PActivity> curActivities = null;
        //当前所有活动任务
        List<JSONObject> curTasks = null;

        if (PPi.PI_STATUS_TEMP.equalsIgnoreCase(pi.getStatus())) {//起草时计算bizInput，如果已保存草稿则不用
            //查询业务输入
            JSONObject processVar = new JSONObject();
            JSONObject variables = ProcessUtil.generateProcessVarialbes(bpmnConfig, null);
            processVar.put(EngineConst.VARIABLES, variables);
            bizInput = ProcessUtil.mappingProcessVarToBizVar(tt.getActivityId(), bpmnConfig, processVar);
            if (bizInput!=null) {
                view.addObject("bizInput", bizInput.toJSONString());
            }
        } else if (PPi.PI_STATUS_DRAFT.equalsIgnoreCase(pi.getStatus())) {

        } else {
            //检查是否有主流程
            if (pi.getParentPieid()!=null && pi.getParentPieid().length()>0) {
                PPi mainPi = dao.selectOneByObject(Mapper.PPiMapper, "selectTopPi", pi);
                urkey.setId(mainPi.getPiEid());
                urkey.setType("0");
                urkey.setUserId(mainPi.getUserId());
                PUserRef urm = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
                pimjo = DhxUtil.toJsonWithDateAli(mainPi, lang, timezoneOffset);
                pimjo.put("userRef", (JSONObject)JSONObject.toJSON(urm));
            }

            //查询业务输入
            String executionId;
            List<Execution> exeList = runtimeService.createExecutionQuery().processInstanceId(pi.getPiEid()).list();
            if (exeList.size() == 1) {
                executionId = exeList.get(0).getId();
            } else {
                // 取得父流程执行实例ID(仅适用于一般子流程)
                executionId = exeList.get(0).getProcessInstanceId();
            }
            logger.debug("getTodoView getting variables of execution " + executionId);
            JSONObject processVar = runtimeService.getVariable(executionId, "processVar", JSONObject.class);
            //bizInput = ProcessUtil.mappingProcessVarToBizVar(tt.getActivityId(), bpmnConfig, processVar);

            //查询当前环节
            Map<String, Object> mpiKeyMap = new HashMap<String, Object>();
            mpiKeyMap.put(Const.TENANT_ID, tenantId);
            mpiKeyMap.put("piId", pi.getPiId());
            curActivities = dao.selectList(Mapper.PActivityMapper, "selectByPiId", mpiKeyMap);
            //查询当前活动任务
            PTaskTodo keytodo = new PTaskTodo();
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                keytodo.setTenantId(Integer.parseInt(tenantId));
            }
            keytodo.setPiId(tt.getPiId());
            List<PTaskTodo> curTodos = dao.selectListByObj(Mapper.PTaskTodoMapper, "selectByObj", keytodo);
            if (curTodos!=null && curTodos.size()>0) {
                curTasks = new ArrayList<JSONObject>();
                for (PTaskTodo td : curTodos) {
                    JSONObject tdjo = DhxUtil.toJsonWithDateAli(td, lang, timezoneOffset);
                    tdjo.put("tiId", AES.encrypt(tdjo.getString("tiId")));
                    curTasks.add(tdjo);
                }
            }

            //返回前更新任务状态
            if (!PPi.PI_STATUS_TEMP.equals(pi.getStatus())
                    && !PPi.PI_STATUS_DRAFT.equals(pi.getStatus())
                    && tt.getReceiveTime() == null) {
                tt.setReceiveTime(DateUtil.getGMTDate());
                tt.setStatus(PTaskTodo.STATUS_READ);	// 任务状态改为已读
                dao.update(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", tt);
            }
        }

        //返回值
        view.addObject("tiId", tiIdEnc);
        view.addObject("formfileName", formfileName);
        view.addObject("formfile", formfile);
        view.addObject("ti", DhxUtil.toJsonWithDateAli(tt, lang, timezoneOffset).toJSONString());
        view.addObject("pi", pijo.toJSONString());
        if (pimjo!=null) {
            view.addObject("mainPi", pimjo.toJSONString());
        }
        view.addObject("bpd", bpd.toJSONString());
        view.addObject("version", version.toJSONString());
        view.addObject("activityConf", activityConf.toJSONString());
        if (curActivities!=null && curActivities.size()>0) {
            view.addObject("curActivities", JSONArray.toJSONString(curActivities));
        }
        if (curTasks!=null && curTasks.size()>0) {
            view.addObject("curTasks", curTasks);
        }
        /*if (bizInput!=null) {
            view.addObject("bizInput", bizInput.toJSONString());
        }*/

        return view;
    }

    /**
     * 任务查看界面
     * @param tenantId
     * @param view
     * @param loginName
     * @param piEidEnc
     * @param contextRealPath
     * @param lang
     * @param timezoneOffset
     * @return
     */
    @Override
    public ModelAndView getPiView(String tenantId, ModelAndView view, String loginName,
                                    String piEidEnc, String doneTiIdEnc,
                                    String contextRealPath, String lang, int timezoneOffset, String formLocation) {
        String piEid = AES.decrypt(piEidEnc);
        String doneTiId = doneTiIdEnc==null || doneTiIdEnc.length()==0 ? null : AES.decrypt(doneTiIdEnc);
        String piId = null;
        String bpdId = null;
        String userId = null;
        String parentPieid = null;
        int versionid = -1;

        PPiAuth authKey = new PPiAuth();
        authKey.setUserId(loginName);

        // 查流程实例 PI
        PPi pi = new PPi();
        PPiHi hi = new PPiHi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
            hi.setTenantId(Integer.parseInt(tenantId));
            authKey.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(piEid);
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);
        if (pi!=null) {
            authKey.setPiId(pi.getPiId());
            piId = pi.getPiId();
            bpdId = pi.getBpdId();
            userId = pi.getUserId();
            versionid = pi.getVersionid();
            parentPieid = pi.getParentPieid();
        } else {//流程已结束
            hi.setPiEid(piEid);
            hi = dao.selectOneByObject(Mapper.PPiHiMapper, "selectByObj", hi);
            if (hi==null) {
                view.setViewName("/html/error-404.jsp");
                return view;
            }
            piId = hi.getPiId();
            bpdId = hi.getBpdId();
            userId = hi.getUserId();
            versionid = hi.getVersionid();
            parentPieid = hi.getParentPieid();
        }

        //校验用户是不是有权限
        int countAuth = dao.selectOne(Mapper.PPiAuthMapper, "countAuth", authKey);
        /*if (countAuth==0) {
            view("/html/error-401.jsp");
            return view;
        }*/

        //流程实例userRef
        PUserRef urkey = new PUserRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            urkey.setTenantId(Integer.parseInt(tenantId));
        }
        urkey.setType("0");
        urkey.setId(piEid);
        urkey.setUserId(userId);
        PUserRef ur = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
        JSONObject pijo = DhxUtil.toJsonWithDateAli(pi!=null ? pi : hi, lang, timezoneOffset);
        pijo.put("userRef", (JSONObject)JSONObject.toJSON(ur));

        //查询version
        JSONObject version = bvcs.getVersionJson(tenantId, bpdId, versionid);
        version.remove(EngineConst.BPMN);   //remove bpmn for performance issue
        //bpmnConfig
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");

        //校验form file
        //如果已办id或已申请的tiId存在，则通过任务找环节的viewFormBinding
        JSONObject formBinding = null;
        if (doneTiId!=null) {
            PTaskDone done = dao.selectOne(Mapper.PTaskDoneMapper, "selectByPrimaryKey", doneTiId);
            formBinding = bpmnConfig.getJSONObject(done.getActivityId()).getJSONObject("viewFormBinding");
        }
        if (formBinding==null) {    //任务未绑定 viewFormBinding
            formBinding = bpmnConfig.getJSONObject("Collaboration_1").getJSONObject("viewFormBinding");
        }
        String tenantSubDir = tenantId == null ? "0/" : tenantId+"/";
        String formfileName = formBinding.getString("formid") + "-" +
                formBinding.getString("versionid") + "-" + lang + "-" +
                env.getProperty("bpmp.formskin");
        String formfile = "/jsp/forms/" + tenantSubDir + formfileName + ".jsp";
        //check form file
        if (!"cdn".equalsIgnoreCase(formLocation)) {
            String realPath = contextRealPath.replace("\\","/") + formfile.substring(1);
            if (logger.isDebugEnabled()) {
                logger.debug("getPiView formfile="+formfile);
                logger.debug("getPiView realPath="+realPath);
            }
            File file = new File(realPath);
            if (!file.exists()) {
                view.setViewName("/html/error-msg.jsp?msg=FORMFILE_NOTFOUND");
                //view.addObject("msg", "FORMFILE_NOTFOUND");
                return view;
            }
        }

        //查询bpd
        JSONObject bpd = bvcs.getBpdSimple(tenantId, bpdId, lang, timezoneOffset);

        //主流程
        JSONObject pimjo = null;

        //检查是否有主流程
        if (parentPieid !=null && parentPieid.length()>0) {
            if (pi!=null) {
                PPi mainPi = dao.selectOneByObject(Mapper.PPiMapper, "selectTopPi", pi);
                if (piEid.equalsIgnoreCase(mainPi.getPiEid())) {
                    pimjo = pijo;
                } else {
                    urkey.setId(mainPi.getPiEid());
                    urkey.setType("0");
                    urkey.setUserId(mainPi.getUserId());
                    PUserRef urm = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
                    pimjo = DhxUtil.toJsonWithDateAli(mainPi, lang, timezoneOffset);
                    pimjo.put("userRef", (JSONObject)JSONObject.toJSON(urm));
                }
            } else {
                Map<String, Object> hikey = new HashMap<String, Object>();
                hikey.put(Const.TENANT_ID, tenantId);
                hikey.put("piId", piId);
                String mainPiEid = null;
                Map<String, Object> mainPi = dao.selectOne(Mapper.PPiMapper, "selectTopPiByHi", hikey);
                if (piEid.equalsIgnoreCase((String)mainPi.get("PI_EID"))) {
                    pimjo = pijo;
                } else {
                    urkey.setId((String)mainPi.get("PI_EID"));
                    urkey.setType("0");
                    urkey.setUserId((String)mainPi.get("USER_ID"));
                    PUserRef urm = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", urkey);
                    pimjo = DhxUtil.toJsonWithDateAli(mainPi, lang, timezoneOffset, true);  //转换mapkey为camelcase
                    pimjo.put("userRef", (JSONObject)JSONObject.toJSON(urm));
                }
            }
        }

        //当前所有活动任务
        List<JSONObject> curTasks = null;
        //查询当前活动任务，与当前流程状态无关
        PTaskTodo keytodo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            keytodo.setTenantId(Integer.parseInt(tenantId));
        }
        keytodo.setPiId(piId);
        List<PTaskTodo> curTodos = dao.selectListByObj(Mapper.PTaskTodoMapper, "selectByObj", keytodo);
        if (curTodos!=null && curTodos.size()>0) {
            curTasks = new ArrayList<JSONObject>();
            for (PTaskTodo td : curTodos) {
                JSONObject tdjo = DhxUtil.toJsonWithDateAli(td, lang, timezoneOffset);
                tdjo.put("tiId", AES.encrypt(tdjo.getString("tiId")));
                curTasks.add(tdjo);
            }
        }

        //当前环节
        List<PActivity> curActivities = null;
        Map<String, Object> mpiKeyMap = new HashMap<String, Object>();
        mpiKeyMap.put(Const.TENANT_ID, tenantId);
        mpiKeyMap.put("piId", piId);
        curActivities = dao.selectList(Mapper.PActivityMapper, "selectByPiId", mpiKeyMap);

        //返回值
        view.addObject("piEid", piEidEnc);
        view.addObject("formfileName", formfileName);
        view.addObject("formfile", formfile);
        view.addObject("pi", pijo.toJSONString());
        if (pimjo!=null) {
            view.addObject("mainPi", pimjo.toJSONString());
        }
        view.addObject("bpd", bpd.toJSONString());
        view.addObject("version", version.toJSONString());
        if (curTasks!=null && curTasks.size()>0) {
            view.addObject("curTasks", curTasks);
        }
        if (curActivities!=null && curActivities.size()>0) {
            view.addObject("curActivities", JSONArray.toJSONString(curActivities));
        }

        return view;
    }

    /**
     * 完成任务
     * @param tenantId
     * @param loginName
     * @param tiId
     * @param params
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject complete(String tenantId, String loginName,
                               String tiId,
                               JSONObject params) {
        JSONObject rtn = new JSONObject();

        //查询任务 TI
        PTaskTodo tt = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            tt.setTenantId(Integer.parseInt(tenantId));
        }
        tt.setTiId(tiId);
        tt = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", tt);

        //校验办理人是不是当前用户
        if (!loginName.equalsIgnoreCase(tt.getUserId()) && !loginName.equalsIgnoreCase(tt.getdUserId())) {
            //TODO 协办人
            rtn.put("msg", "401");
        }

        //PI
        /*PPi pi = new PPi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) pi.setTenantId(Integer.parseInt(tenantId));
        pi.setPiEid(tt.getPiEid());
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);*/
        JSONObject pijo = params.getJSONObject("pi");
        String bpdId = pijo.getString("bpdId");
        int versionid = pijo.getInteger("versionid");
        String status = pijo.getString("status");
        JSONObject version = bvcs.getVersionJson(tenantId, bpdId, versionid);
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");

        if (PPi.PI_STATUS_TEMP.equalsIgnoreCase(status)
                || PPi.PI_STATUS_DRAFT.equalsIgnoreCase(status)) { //需发起
            rtn = pis.startProcessByDraft(tenantId, tt, bpmnConfig, params);
        } else { //办理

            boolean completeEngineTask = ProcessUtil.isCompleteEngineTask(dao, tt);

            //后加签特殊处理
            if (PTaskTodo.TYPE_INVOLVED_AFTER.equals(tt.gettType())) {
                //判断来源任务是否是驳回操作
                PTaskDone srcDone = new PTaskDone();
                if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                    srcDone.setTenantId(Integer.parseInt(tenantId));
                }
                srcDone.setTiId(tt.getPrevTiid());
                srcDone = dao.selectOne(Mapper.PTaskDoneMapper, "selectByObj", srcDone);
                if (PTaskTodo.TYPE_COMPETE.equals(srcDone.gettType()) || PTaskTodo.TYPE_NORMAL.equals(srcDone.gettType())) {
                    if (PTaskDone.OPERATION_RETURN == srcDone.getOperation()) {
                        //从流程变量取驳回环节
                        String targetActivityId = (String)runtimeService.getVariable(tt.getPiEid(), EngineConst.PV_RETURN_ACTID_AFTERINVOLVE+tt.getActivityId());
                        if (targetActivityId!=null) {
                            params.put(EngineConst.TARGET_ACTIVITY, targetActivityId);
                            runtimeService.removeVariable(tt.getPiEid(), EngineConst.PV_RETURN_ACTID_AFTERINVOLVE+tt.getActivityId());
                        }
                        return doReturnTask(tenantId, tt, bpmnConfig, params);
                    }
                }
            }

            //普通完成
            rtn = completeTask(tenantId, tt, bpmnConfig, params, completeEngineTask);
        }

        return rtn;
    }

    /**
     * 完成任务
     * @param tenantId
     * @param todo
     * @param bpmnConfig
     * @param params
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject completeTask(String tenantId, PTaskTodo todo, JSONObject bpmnConfig, JSONObject params, boolean completeEngineTask) {
        JSONObject rtn = new JSONObject();

        int operation = PTaskDone.OPERATION_SUBMIT;
        if (params.containsKey(EngineConst.OPERATION)) {
            operation = params.getInteger(EngineConst.OPERATION);
        }

        //待办转为已办
        PTaskDone done = ProcessUtil.todoToDone(todo);
        done.setOperation(operation);
        int rs = dao.insert(Mapper.PTaskDoneMapper, "insertSelective", done);
        //删除待办
        rs += dao.delete(Mapper.PTaskTodoMapper, "deleteByObj", todo);

        //意见
        String comments = params.getString("comments");
        if (comments!=null && comments.length()>0) {
            PComment comment = new PComment();
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                comment.setTenantId(Integer.parseInt(tenantId));
            }
            comment.setId(UUID.randomUUID().toString());
            comment.setComment(comments);
            comment.setUserId(todo.getUserId());
            comment.setUserName(todo.getUserName());
            comment.setPiId(todo.getPiId());
            comment.setTiId(todo.getTiId());
            comment.setAddTime(DateUtil.getGMTDate());
            rs += dao.insert(Mapper.PCommentMapper, "insertSelective", comment);
        }

        // 完成引擎中任务
        if (completeEngineTask) {
            List<Execution> eql = null;
            if (tenantId!=null && tenantId.length()>0) {
                eql = runtimeService.createExecutionQuery().processInstanceId(todo.getPiEid()).executionTenantId(tenantId).list();
            } else {
                eql = runtimeService.createExecutionQuery().processInstanceId(todo.getPiEid()).list();
            }
            String executionId = null;
            for (Execution eq : eql) {
                String activityId = eq.getActivityId();
                if (!"".equals(activityId) && null != activityId) {
                    executionId = eq.getId();
                }
            }

            logger.debug("getting variables of execution " + executionId);
            JSONObject processVar = runtimeService.getVariable(executionId, EngineConst.PROCESSVAR, JSONObject.class);
            JSONObject bizOutputJson = params.getJSONObject("bizOutput");
            ProcessUtil.mappingBizVarToProcessVar(todo.getActivityId(), bpmnConfig, processVar, bizOutputJson);

            // 计算网关变量
            logger.debug("completing engine task... bizOutput=" + bizOutputJson);
            ProcessUtil.calculateGatewayVar(todo.getActivityId(), bpmnConfig, processVar);
            Map<String, Object> completeArg = new HashMap<String, Object>();
            completeArg.put(EngineConst.PROCESSVAR, processVar);
//            completeArg.put(EngineConst.PV_LAST_OPERATION, operation);

            // 完成引擎中任务
            logger.debug("completing engine task... processVar=" + processVar);
            taskService.complete(todo.getTiEId(), completeArg);
//            runtimeService.removeVariable(todo.getPiEid(), EngineConst.PV_LAST_OPERATION);
//            logger.debug("runtime variables:"+runtimeService.getVariables(todo.getPiEid()));
            logger.debug("completed engine task... tiid=" + todo.getTiId());
        }

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 驳回
     * @param tenantId
     * @param loginName
     * @param displayName
     * @param tiId
     * @param params
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject returnTask(String tenantId, String loginName, String displayName, String tiId,
                                 JSONObject params) {
        JSONObject rtn = new JSONObject();

        //查询任务 TI
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setTiId(tiId);
        todo = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", todo);

        JSONObject pijo = params.getJSONObject("pi");
        String bpdId = pijo.getString("bpdId");
        int versionid = pijo.getInteger("versionid");
        JSONObject version = bvcs.getVersionJson(tenantId, bpdId, versionid);
        JSONObject bpmnConfig = version.getJSONObject("bpmnConfig");

        //判断是否需要完成引擎任务
        String completeEngineTask = Boolean.TRUE.toString();
        List<Map<String, Object>> counts = dao.selectObjListByObj(Mapper.PTaskTodoMapper, "countOtherTasksGroupByType", todo);
        if (counts==null || counts.size()==0) {
            completeEngineTask = Boolean.TRUE.toString();  //无其他任务
        } else {
            Map<String, Integer> cmap = new HashMap<String, Integer>();
            int total = 0;
            for (Map<String, Object> crow : counts) {
                String tType = (String)crow.get("T_TYPE");
                Long count = (Long)crow.get("COUNT");
                cmap.put(tType, count.intValue());
                total += count.intValue();
            }

            switch (todo.gettType()) {
                case PTaskTodo.TYPE_NORMAL:
                    //same as TYPE_COMPETE
                case PTaskTodo.TYPE_COMPETE:
                    dao.delete(Mapper.PTaskTodoMapper, "deleteSameTypeOtherTodosOnActivity", todo);
                    if (cmap.containsKey(PTaskTodo.TYPE_INVOLVED_AFTER) && cmap.get(PTaskTodo.TYPE_INVOLVED_AFTER)>0) {
                        //存在后加签，直接转为已办
                        PTaskDone done = ProcessUtil.todoToDone(todo);
                        done.setOperation(PTaskDone.OPERATION_RETURN);
                        dao.insert(Mapper.PTaskDoneMapper, "insert", done);
                        dao.delete(Mapper.PTaskTodoMapper, "deleteByPrimaryKey", todo);
                        //将驳回环节记录到变量
                        runtimeService.setVariable(todo.getPiEid(), EngineConst.PV_RETURN_ACTID_AFTERINVOLVE+todo.getActivityId(), params.getString(EngineConst.TARGET_ACTIVITY));
                        completeEngineTask = "DONE";
                    } else if (cmap.containsKey(PTaskTodo.TYPE_INVOLVED_SYNC) && cmap.get(PTaskTodo.TYPE_INVOLVED_SYNC)>0) {
                        //存在并加签
                        completeEngineTask = "HAS_INVOLVE_SYNC";
                    } else {
                        completeEngineTask = Boolean.TRUE.toString();
                    }
                    break;
                case PTaskTodo.TYPE_INVOLVED_BEFORE:
                    completeEngineTask = "NOT_PERMITTED";
                    break;
                case PTaskTodo.TYPE_INVOLVED_AFTER:
                    completeEngineTask = "NOT_PERMITTED";
                    break;
                case PTaskTodo.TYPE_CONSIGN:
                    completeEngineTask = "NOT_PERMITTED";
                    break;
                case PTaskTodo.TYPE_INVOLVED_SYNC:
                    completeEngineTask = "NOT_PERMITTED";
                    break;
                default:
                    logger.warn("returnTask default case is invalid.");
            }
        }

        if (Boolean.TRUE.toString().equalsIgnoreCase(completeEngineTask)) {
            return doReturnTask(tenantId, todo, bpmnConfig, params);
        } else if ("DONE".equalsIgnoreCase(completeEngineTask)) {
            rtn.put(Const.AJAX_RESULT, 1);
            return rtn;
        } else {
            rtn.put(Const.AJAX_MSG, completeEngineTask);
            return rtn;
        }

    }

    private JSONObject doReturnTask(String tenantId, PTaskTodo todo, JSONObject bpmnConfig, JSONObject params) {
        JSONObject rtn = null;
        // 取得当前任务
        HistoricTaskInstance currTask = historyService.createHistoricTaskInstanceQuery().taskId(todo.getTiEId()).singleResult();
        // 取得流程实例
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(currTask.getProcessInstanceId()).singleResult();
        if (instance == null) {
            logger.error("process returnTask error, ProcessInstance not found, activityId=" + todo.getActivityId());
            throw new RuntimeException(new EngineException("", "processDefination not found! while doing returnTask, activityId=" + todo.getActivityId()));
        }
        // 取得流程定义
        ProcessDefinitionEntity defination = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(currTask.getProcessDefinitionId());
        if (defination == null) {
            logger.error("process returnTask error, ProcessDefinitionEntity not found, activityId=" + todo.getActivityId());
            throw new RuntimeException(new EngineException("", "processDefination not found! while doing returnTask, activityId=" + todo.getActivityId()));
        } else {
            // 取得上一步活动
            ActivityImpl currentActiviti = ((ProcessDefinitionImpl) defination).findActivity(todo.getActivityId());
            // 清除当前活动的出口
            List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
            List<PvmTransition> pvmTransitionList = currentActiviti.getOutgoingTransitions();
            for (PvmTransition pvmTransition : pvmTransitionList) {
                oriPvmTransitionList.add(pvmTransition);
            }
            pvmTransitionList.clear();

            // 建立新出口
            List<TransitionImpl> newTransitions = new ArrayList<TransitionImpl>();
            ActivityImpl nextActivityImpl = ((ProcessDefinitionImpl) defination).findActivity(params.getString(EngineConst.TARGET_ACTIVITY));
            //String tempSeqflowId = EngineConst.ID_SEQFLOW_TEMP+UUID.randomUUID();
            TransitionImpl newTransition = currentActiviti.createOutgoingTransition();
            newTransition.setDestination(nextActivityImpl);
            newTransitions.add(newTransition);
            // 完成任务,包括引擎中的任务
            rtn = completeTask(tenantId, todo, bpmnConfig, params, true);
            // historyService.deleteHistoricTaskInstance(tiEId);
            // -----------------------------------------------------------
            // 项目中业务表的操作
            // 1 删除任务待办表
            // 2 插入任务历史表
            // 3 插入驳回待办任务

            // 恢复方向
            for (TransitionImpl transitionImpl : newTransitions) {
                currentActiviti.getOutgoingTransitions().remove(transitionImpl);
            }
            for (PvmTransition pvmTransition : oriPvmTransitionList) {
                pvmTransitionList.add(pvmTransition);
            }

            // 办理任务时记录操作日志 TODO

            return rtn;
        }
    }

    /**
     * 转办
     * @param tenantId
     * @param loginName
     * @param displayName
     * @param tiId
     * @param assignTo
     * @param assignToName
     * @param comment
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject reassignTask(String tenantId, String loginName, String displayName, String tiId,
                                   String assignTo, String assignToName, String comment) {
        JSONObject rtn = new JSONObject();
        int rs = 0;
        //查询任务 TI
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setTiId(tiId);
        todo = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", todo);

        //check
        if (!todo.getUserId().equalsIgnoreCase(loginName) &&
                !loginName.equalsIgnoreCase(todo.getdUserId())) {
            rtn.put(Const.AJAX_RESULT, 0);
            return rtn;
        }

        //done
        PTaskDone done = ProcessUtil.todoToDone(todo);
        done.setOperation(PTaskDone.OPERATION_REASSIGN);

        //new to-do
        BeanCopier copier = BeanCopier.create(PTaskTodo.class, PTaskTodo.class, false);
        PTaskTodo newTodo = new PTaskTodo();
        copier.copy(todo, newTodo, null);
        String newTiId = "TID-"+UUID.randomUUID();
        newTodo.setTiId(newTiId);
        newTodo.setUserId(assignTo);
        newTodo.setUserName(assignToName);
        newTodo.setPrevTiid(done.getTiId());
        //PUserRef
        Map<String, Object> asMap = new HashMap<String, Object>();
        asMap.put(Const.TENANT_ID, tenantId);
        asMap.put("userIds", assignTo);
        Map<String, Object> assigneeMap = dao.selectOne(Mapper.AssigneeMapper, "selectAssigneeByUserIds", asMap);
        PUserRef ref = new PUserRef();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            ref.setTenantId(Integer.parseInt(tenantId));
        }
        ref.setId(newTiId);
        ref.setType(PUserRef.TYPE_TASK);
        ref.setUserId(assignTo);
        ref.setUserName(assignToName);
        ref.setDeptId((String)assigneeMap.get("ORG_ID"));
        ref.setDeptName((String)assigneeMap.get("ORG_NAME"));

        //PPiAuth
        PPiAuth auth = new PPiAuth();
        //comment
        PComment comment1 = new PComment();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            auth.setTenantId(Integer.parseInt(tenantId));
            comment1.setTenantId(Integer.parseInt(tenantId));
        }
        auth.setAuthType(PPiAuth.AUTH_TYPE_TASKOWNER);
        auth.setUserId(assignTo);
        auth.setPiId(todo.getPiId());

        comment1.setAddTime(DateUtil.getGMTDate());
        comment1.setUserId(loginName);
        comment1.setUserName(displayName);
        comment1.setComment(comment);
        comment1.setTiId(done.getTiId());
        comment1.setPiId(done.getPiId());
        comment1.setId(UUID.randomUUID().toString());

        //生成已办
        rs += dao.insert(Mapper.PTaskDoneMapper, "insert", done);
        //删除旧待办
        rs += dao.delete(Mapper.PTaskTodoMapper, "deleteByPrimaryKey", todo);
        //生成新待办
        rs += dao.insert(Mapper.PTaskTodoMapper, "insert", newTodo);
        //插入PUserRef
        rs += dao.insert(Mapper.PUserRefMapper, "insert", ref);
        //插入PPiAuth
        rs += dao.insert(Mapper.PPiAuthMapper, "insertOrUpdate", auth);
        //插入comment
        rs += dao.insert(Mapper.PCommentMapper, "insert", comment1);

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 加签
     * @param tenantId
     * @param loginName
     * @param displayName
     * @param tiId
     * @param comment
     * @param involveType
     * @param involedUsers
     * @param params
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject involve(String tenantId, String loginName, String displayName,
                              String tiId, String comment, String involveType, String involedUsers, JSONObject params) {
        JSONObject rtn = new JSONObject();
        int rs = 0;

        //加签用户
        JSONArray inUsers = JSONArray.parseArray(involedUsers);
        if (inUsers==null || inUsers.size()==0) {
            rtn.put(Const.AJAX_RESULT, 0);
            return rtn;
        }

        //查询任务 TI
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setTiId(tiId);
        todo = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", todo);

        //check
        if (!todo.getUserId().equalsIgnoreCase(loginName) &&
                !loginName.equalsIgnoreCase(todo.getdUserId())) {
            rtn.put(Const.AJAX_RESULT, 0);
            return rtn;
        }

        //生成一个加签任务
        String doneTiId = ProcessUtil.newId("TI_ID");
        PTaskDone done = ProcessUtil.todoToDone(todo);
        done.setOperation(PTaskDone.OPERATION_INVOLVE);
        done.setTiId(doneTiId);
        PUserRef ref = new PUserRef();
        PComment comment1 = new PComment();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            ref.setTenantId(Integer.parseInt(tenantId));
            comment1.setTenantId(Integer.parseInt(tenantId));
        }
        ref.setId(tiId);
        ref.setType(PUserRef.TYPE_TASK);
        ref.setUserId(done.getUserId());
        ref = dao.selectOne(Mapper.PUserRefMapper, "selectByPrimaryKey", ref);
        ref.setId(doneTiId);
        comment1.setAddTime(DateUtil.getGMTDate());
        comment1.setUserId(loginName);
        comment1.setUserName(displayName);
        comment1.setComment(comment);
        comment1.setTiId(doneTiId);
        comment1.setPiId(done.getPiId());
        comment1.setId(UUID.randomUUID().toString());

        //生成新待办
        BeanCopier copier = BeanCopier.create(PTaskTodo.class, PTaskTodo.class, false);
        List<PTaskTodo> todos = new ArrayList<PTaskTodo>();
        List<PPiAuth> auths = new ArrayList<PPiAuth>();
        List<PUserRef> refs = new ArrayList<PUserRef>();
        for (int i=0; i<inUsers.size(); i++) {
            JSONObject userObj = inUsers.getJSONObject(i);
            PTaskTodo newTodo = new PTaskTodo();
            copier.copy(todo, newTodo, null);
            newTodo.setTiId(ProcessUtil.newId("TI_ID"));
            if ("parallel".equalsIgnoreCase(involveType)) {
                newTodo.settType(PTaskTodo.TYPE_INVOLVED_SYNC);
            } else if ("pre".equalsIgnoreCase(involveType)) {
                newTodo.settType(PTaskTodo.TYPE_INVOLVED_BEFORE);
            } else if ("post".equalsIgnoreCase(involveType)) {
                newTodo.settType(PTaskTodo.TYPE_INVOLVED_AFTER);
                newTodo.setStatus(PTaskTodo.STATUS_HIDE);   //后加签需要当前人办理后启用
            }
            newTodo.setReceiveTime(null);
            newTodo.setCreateTime(DateUtil.getGMTDate());
            newTodo.setStatus(PTaskTodo.STATUS_NEW);
            newTodo.setUserId(userObj.getString("realValue"));
            newTodo.setUserName(userObj.getString("displayValue"));
            newTodo.setdUserId(null);
            newTodo.setdUserName(null);
            newTodo.setPrevTiid(todo.getTiId());
            todos.add(newTodo);

            PPiAuth auth = new PPiAuth();
            PUserRef newRef = new PUserRef();
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                auth.setTenantId(Integer.parseInt(tenantId));
                newRef.setTenantId(Integer.parseInt(tenantId));
            }

            auth.setAuthType(PPiAuth.AUTH_TYPE_TASKOWNER);
            auth.setUserId(newTodo.getUserId());
            auth.setPiId(newTodo.getPiId());
            auths.add(auth);

            Map<String, Object> asMap = new HashMap<String, Object>();
            asMap.put(Const.TENANT_ID, tenantId);
            asMap.put("userIds", newTodo.getUserId());
            List<Map<String, Object>> list = dao.selectList(Mapper.AssigneeMapper, "selectAssigneeByUserIds", asMap);
            Map<String, Object> assigneeMap = list.get(0);
            newRef.setId(newTodo.getTiId());
            newRef.setType(PUserRef.TYPE_TASK);
            newRef.setUserId(newTodo.getUserId());
            newRef.setUserName(newTodo.getUserName());
            newRef.setDeptId((String)assigneeMap.get("ORG_ID"));
            newRef.setDeptName((String)assigneeMap.get("ORG_NAME"));
            refs.add(newRef);
        }

        //插入加签信息
        rs += dao.batchInsert(Mapper.PTaskTodoMapper, "insert", todos);
        rs += dao.batchInsert(Mapper.PUserRefMapper, "insert", refs);
        rs += dao.batchInsert(Mapper.PPiAuthMapper, "insertOrUpdate", auths);

        //生成已办
        rs += dao.insert(Mapper.PTaskDoneMapper, "insert", done);
        //为已办插入PUserRef
        rs += dao.insert(Mapper.PUserRefMapper, "insert", ref);
        //插入comment
        rs += dao.insert(Mapper.PCommentMapper, "insert", comment1);

        //原任务状态变为隐藏
        if ("pre".equalsIgnoreCase(involveType)) {
            todo.setStatus(PTaskTodo.STATUS_HIDE);
            rs += dao.update(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", todo);
        } else if ("post".equalsIgnoreCase(involveType)) {
            /*//完成当前任务
            rs += dao.delete(Mapper.PTaskTodoMapper, "deleteByPrimaryKey", todo);
            PUserRef delRef = new PUserRef();
            PPi pi = new PPi();
            if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
                delRef.setTenantId(Integer.parseInt(tenantId));
                pi.setTenantId(Integer.parseInt(tenantId));
            }
            ref.setId(todo.getTiId());
            ref.setType(PUserRef.TYPE_TASK);
            ref.setUserId(todo.getUserId());
            rs += dao.delete(Mapper.PUserRefMapper, "deleteByPrimaryKey", delRef);

            pi.setPiEid(todo.getPiEid());
            pi = dao.selectOne(Mapper.PPiMapper, "selectByObj", pi);
            JSONObject version = bvcs.getVersionJson(tenantId, pi.getBpdId(), pi.getVersionid());

            JSONObject compRtn = completeTask(tenantId, todo, version.getJSONObject(EngineConst.BPMNCONF), params);
            if (logger.isDebugEnabled()) {
                logger.debug("completeTask returns: "+ (compRtn==null? null : compRtn.toJSONString()));
            }*/
        }

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 延迟办理
     * @param tenantId
     * @param loginName
     * @param tiId
     * @return
     */
    @Override
    public JSONObject defer(String tenantId, String loginName, String tiId) {
        JSONObject rtn = new JSONObject();

        //查询任务 TI
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setTiId(tiId);
        todo = dao.selectOneByObject(Mapper.PTaskTodoMapper, "selectByObj", todo);

        //check
        if (!todo.getUserId().equalsIgnoreCase(loginName) &&
                !loginName.equalsIgnoreCase(todo.getdUserId())) {
            rtn.put(Const.AJAX_RESULT, 0);
            return rtn;
        }

        todo.setPriority(PTaskTodo.PRIORITY_DEFERED);
        int rs = dao.update(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", todo );

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 认领任务
     * @param tenantId
     * @param loginName
     * @param tiId
     * @param piEid
     * @param activityId
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject claim(String tenantId, String loginName, String tiId, String piEid, String activityId) {
        JSONObject rtn = new JSONObject();

        //查询任务 TI
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setPiEid(piEid);
        todo.setActivityId(activityId);
        List<PTaskTodo> todos = dao.selectListByObj(Mapper.PTaskTodoMapper, "selectByObj", todo);

        //submitted by other
        if (todos==null || todos.size()==0) {
            rtn.put(Const.AJAX_RESULT, 0);
            rtn.put("msg", "CLAIMED_BY_OTHER");
            return rtn;
        }

        for (PTaskTodo t : todos) {
            if (tiId.equalsIgnoreCase(t.getTiId())) {
                //check privilege
                if (!t.getUserId().equalsIgnoreCase(loginName) &&
                        !loginName.equalsIgnoreCase(t.getdUserId())) {
                    rtn.put(Const.AJAX_RESULT, 0);
                    return rtn;
                }
                if (PTaskTodo.STATUS_HIDE.equals(t.getStatus())) {
                    rtn.put(Const.AJAX_RESULT, 0);
                    rtn.put("msg", "CLAIMED_BY_OTHER");
                    return rtn;
                }
                t.setStatus(PTaskTodo.STATUS_CLAIMED);
            } else {
                t.setStatus(PTaskTodo.STATUS_HIDE);
            }
        }

        int rs = dao.batchUpdate(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", todos );

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /**
     * 退领任务
     * @param tenantId
     * @param loginName
     * @param tiId
     * @param piEid
     * @param activityId
     * @return
     */
    @Override
    @Transactional(EngineConst.TRANSACTION_MANAGER)
    public JSONObject unclaim(String tenantId, String loginName, String tiId, String piEid, String activityId) {
        JSONObject rtn = new JSONObject();

        //查询任务 TI
        PTaskTodo todo = new PTaskTodo();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            todo.setTenantId(Integer.parseInt(tenantId));
        }
        todo.setPiEid(piEid);
        todo.setActivityId(activityId);
        List<PTaskTodo> todos = dao.selectListByObj(Mapper.PTaskTodoMapper, "selectByObj", todo);

        for (PTaskTodo t : todos) {
            if (tiId.equalsIgnoreCase(t.getTiId())) {
                //check privilege
                if (!t.getUserId().equalsIgnoreCase(loginName) &&
                        !loginName.equalsIgnoreCase(t.getdUserId())) {
                    rtn.put(Const.AJAX_RESULT, 0);
                    return rtn;
                }
            }
            if (t.getReceiveTime()==null) {
                t.setStatus(PTaskTodo.STATUS_NEW);
            } else {
                t.setStatus(PTaskTodo.STATUS_READ);
            }
        }

        int rs = dao.batchUpdate(Mapper.PTaskTodoMapper, "updateByPrimaryKeySelective", todos );

        rtn.put(Const.AJAX_RESULT, rs);
        return rtn;
    }

    /*@Override
    public JSONArray getRejectingActivities(String tenantId, String tiId, String activityId, String piEid) {
        PPi pi = new PPi();
        PPiHi hi = new PPiHi();
        if (tenantId!=null && StringUtils.isNumeric(tenantId)) {
            pi.setTenantId(Integer.parseInt(tenantId));
        }
        pi.setPiEid(piEid);
        pi = dao.selectOneByObject(Mapper.PPiMapper, "selectByObj", pi);
        JSONObject version = bvcs.getVersionJson(tenantId, pi.getBpdId(), pi.getVersionid());
        return (JSONArray) JSONPath.eval(version, "$.bpmnConfig."+activityId+".rejectPathSelect");
    }*/

}
